-- 刷新点预测
local sf = require "widgets/hx_starfish"
local e_util = require "libs/entutil"
local t_util = require "libs/tableutil"
local c_util = require "libs/calcutil"
local h_util = require "libs/hudutil"
local _G = GLOBAL
local range = 60
local file_data = require("QAQ/layoutdata")
local re_ps = require("QAQ/rangetable").starfish_range
local starLouts,starPrefabs,showPrefabs = file_data.star, file_data.sp, file_data.ssp
local star_id = "starfish"
local w_data, ents_tile, ents_record= {}, {}, {}
local _lock
AddPlayerActivatedFunc(function(player, world)
    if not _lock then
        _lock = true
        local iscave = world:HasTag("cave") and "cave" or "world"
        w_data = require("libs/worldmanager"):GetWorldDataList(star_id..iscave)
    end
end)
local function scantile(star, value)
    if not value then
        t_util:Pairs(ents_tile, function(_, ent)
            if ent then ent:Remove() end
        end)
        ents_tile = {}
        return
    end
    local w_util = require "libs/worldutil"
    local map_tile, map_pos = w_util:GetMapTiles()
    if not _G.TheWorld.Map or not _G.ThePlayer then return end
    local x_c,_,z_c = _G.TheWorld.Map:GetTileCenterPoint(_G.ThePlayer:GetPosition():Get())
    t_util:IPairs(starLouts, function(src)
        local LayoutData = w_util:GetLayoutData(src)
        local ground_data = LayoutData and LayoutData.ground_types and LayoutData.ground
        local layout = LayoutData.layout
        if not ground_data then return end
        local count_lines,count_cols = #ground_data,#(ground_data[1])

        local choice = {}
        local max_calc = 0

        for x = x_c-range, x_c+range,4 do
            for y = z_c-range, z_c+range,4 do
                local ori = {1, 2, 3, 4, 5, 6, 7, 8}
                if not t_util:GetElement(ground_data, function(line, linedata)
                    return t_util:GetElement(linedata, function(col, tiledata)
                        local tile_layout = tiledata and LayoutData.ground_types[tiledata]
                        if not tile_layout then return end

                        local lt_x, lt_y = (line - 1) * 4, (col - 1) * 4
                        t_util:Pairs(ori, function(mode, value)
                            if not value or tile_layout==0 then
                                return
                            end
                            local vx, vy = c_util:ModeToXY(mode, lt_x, lt_y, x, y)
                            local tile_world = map_pos[vx] and map_pos[vx][vy]
                            if not tile_world or tile_layout~=tile_world
                            then
                                ori[mode] = false
                            end
                        end)
                        if not t_util:GetElement(ori, function(mode, value)
                            return value
                        end) then
                            return true
                        end
                    end)
                end) then
                    t_util:Pairs(ori, function(mode, value)
                        local cx, cy = c_util:ModeToXY(mode, (count_lines-1)*2, (count_cols-1)*2, x, y)
                        local cp,sum = 0,0
                        t_util:Pairs(starPrefabs, function(key, prefab)
                            local name = type(key) == "number" and prefab or key
                            local poses = (name and layout[name]) or {}
                            if #poses == 0 then return end
                            local has = 0
                            cp = cp+1
                            t_util:IPairs(poses, function(pos)
                                local nx,ny = c_util:ModeToXY(mode, pos.x*4, pos.y*4, cx, cy)
                                if not e_util:FindEnt({x=nx, z=ny}, prefab, 2) then return end
                                has = has+1
                            end)
                            sum = sum + has/#poses
                        end)
                        local calc = sum/cp
                        if calc > max_calc and c_util:GetDist(cx, cy, x_c, z_c) < 30 and calc>0.5 then
                            max_calc = calc
                            choice = {
                                x = cx, y = cy, m = mode, c = max_calc, l = layout, s = src,
                            }
                        end
                    end)
                end
            end
        end
        if choice.m then
            t_util:Pairs(showPrefabs, function(name, prefab)
                t_util:IPairs(choice.l[name] or {}, function(pos)
                    local nx,ny = c_util:ModeToXY(choice.m, pos.x*4, pos.y*4, choice.x, choice.y)
                    local id = nx..ny 
                    local icon = prefab and re_ps[prefab]
                    if not ents_tile[id] and icon then
                        ents_tile[id] = e_util:SpawnFx(prefab, icon.build, icon.bank, icon.anim, _G.WEBCOLOURS.BLUE)
                        ents_tile[id].Transform:SetPosition(nx, 0, ny)
                    end
                end)
            end)
        end
    end)
end
local function scanrecord(star, value)
    if not value then
        t_util:Pairs(ents_record, function(_, ent)
            if ent then ent:Remove() end
        end)
        ents_record = {}
        return
    end
    if not _G.TheWorld.Map or not _G.ThePlayer then return end
    local x_c,_,z_c = _G.TheWorld.Map:GetTileCenterPoint(_G.ThePlayer:GetPosition():Get())
    t_util:Pairs(w_data, function(id, data)
        if data and c_util:GetDist(x_c, z_c, data.x, data.y) < range then
            local id = data.x..data.y
            local icon = re_ps[data.p]
            if not ents_record[id]and icon then
                ents_record[id] = e_util:SpawnFx(data.p, icon.build, icon.bank, icon.anim, _G.WEBCOLOURS.ORANGE)
                ents_record[id].Transform:SetPosition(data.x, 0, data.y)
            end
        end
    end)
end
-- 记忆存储
t_util:Pairs(re_ps, function(prefab)
    AddPrefabPostInit(prefab, function(inst)
        inst:DoTaskInTime(0.5, function(inst)
            if e_util:IsAnim({"idle", "idle_loop", "enter"}, inst) then
                local x,y = e_util:GetSavePos(inst)
                t_util:Pairs(w_data, function(id, data)
                    if data and data.p==prefab and c_util:GetDist(data.x, data.y, x, y) < 4 then
                        w_data.id = nil
                    end
                end)
                table.insert(w_data, {
                    x = x,
                    y = y,
                    p = prefab
                })
            end
        end)
    end)
end)
local directions = {  
    {0, 1}, -- 上  
    {0, -1}, -- 下  
    {1, 0}, -- 右  
    {-1, 0}, -- 左  
    {1, 1}, -- 右上  
    {1, -1}, -- 左上  
    {-1, 1}, -- 右下  
    {-1, -1} -- 左下  
}  
local distance = 1.24
local function stardeploy()
    local geo_fn = _G.MOD_ShroomCake.Func.SetGeoCTRL
    if type(geo_fn)=="function" then
        geo_fn(not _G.TheInput:IsKeyDown(_G.KEY_CTRL))
    end
    local p_util = require "libs/playerutil"
    local needprefab = "dug_trap_starfish"
    local star = p_util:GetItemFromAll(needprefab, nil, nil, "hands")
    if not star then
        return TIP("海星清远古", "red", "缺少海星")
    end
    local act_item = p_util:GetActiveItem()
    if act_item then
        if act_item.prefab ~= needprefab then
            local newit = p_util:GetSlotFromAll(needprefab)
            if newit then
                local cont = e_util:GetContainer(newit.container)
                if cont then
                    cont:SwapActiveItemWithSlot(newit.slot) 
                end
            end
        end
    else
        local newit = p_util:GetSlotFromAll(needprefab)
        if newit then
            local cont = e_util:GetContainer(newit.container)
            if cont then cont:TakeActiveItemFromAllOfSlot(newit.slot) end
        end
    end
    _G.ThePlayer:DoTaskInTime(0.2, function(player)
        act_item = p_util:GetActiveItem(needprefab)
        local inventoryitem = act_item and act_item.replica and act_item.replica.inventoryitem
        if not inventoryitem then
            return TIP("海星清远古", "red", "无法拿起海星")
        else
            local prefabs = {"bishop_nightmare", "rook_nightmare", "knight_nightmare", "minotaur"}
            if e_util:IsHuxi() then table.insert(prefabs, "antlion") end
            local fx = e_util:FindEnt(player, nil, nil, e_util:GetFxTags(), {"inlimbo", "player"}, nil, nil, function(ent)
                local xp, _, yp = ent:GetPosition():Get()
                return ent.hxname and table.contains(prefabs, ent.hxname) and t_util:GetElement(directions, function(_, dir)
                    local x,y = xp + dir[1], yp + dir[2]
                    local pos_new = c_util:GetIntersectPotRadiusPot(ent:GetPosition(), distance, _G.Vector3(x, 0, y))
                    return inventoryitem:CanDeploy(pos_new, nil, player)
                end)
            end)
            local pos_fx = fx and fx:GetPosition()
            if not pos_fx then
                return TIP("海星清远古", "red", "没有可用生成点")
            end
            local xp,_,yp = pos_fx:Get()
            local pos = t_util:GetElement(directions, function(_, dir)
                local x,y = xp + dir[1], yp + dir[2]
                local pos_new = c_util:GetIntersectPotRadiusPot(pos_fx, distance, _G.Vector3(x, 0, y))
                return inventoryitem:CanDeploy(pos_new, nil, player) and pos_new
            end)
            if not pos then
                return TIP("海星清远古", "red", "没有可用生成点")
            end
            if inventoryitem:CanDeploy(pos, nil, player) then
                local act = _G.BufferedAction(player, nil, _G.ACTIONS.DEPLOY, act_item, pos)
                local playercontroller = player.components.playercontroller
                if playercontroller.deployplacer then
                    act.rotation = playercontroller.deployplacer.Transform:GetRotation()
                end
                p_util:DoAction(act, _G.RPC.RightClick, act.action.code, pos.x, pos.z, nil, act.rotation, true, nil, nil, act.action.mod_name)
            end
        end
    end)
end

local function fn()
    local hud = _G.ThePlayer.HUD
    if hud.hx_starfish then
        hud.hx_starfish:Kill()
        hud.hx_starfish = nil
    else
        local funcs = {
            Tile = scantile,
            Record = scanrecord,
            Deploy = stardeploy,
        }
        hud.hx_starfish = hud:AddChild(sf(funcs))
    end
end


-- 面板功能


if GetModConfigData("sw_starfish") == "biubiu" then 
    DEAR_BTNS:AddDearBtn("minimap/minimap_data.xml", "star_trap.png", "刷新点预测", "清远古用，扫描地形自动分析", true, fn)
end
AddBindBtn("sw_starfish", fn)